#include "APnMApp.h"
#include <QtXml/QDomElement>
#include "APnMScene.h"
#include "gactiveelement.h"
#include "gpool.h"
#include "link.h"
#include "delement.h"
#include "DElementDelegate.h"

/************************************************************************
** Problems: qgraphicsitem_cast can only cast objects to its real objects.
**       vs. dynamic_cast: all objects on the hierarchy of it.
************************************************************************/ 

/************************************************************************
** QGraphisItem's Destructor: 
** If this item is currently associated with a scene, 
** the item will be removed from the scene before it is deleted. 
************************************************************************/


APnMApp::APnMApp(QObject *parent)
	: QObject(parent), mScene(NULL)
{
}

/************************************************************************/
/*	The selectedItems maybe contain many GElements and Links
/*	For the consistent of the developed APnModel Application,
/*	Links should be deleted first to avoid suspended pointers in
/*	selectedItems, because Links' destructor will delete
/*	their self-reference from their related GElemetns, not GElements.
/*	But if delete GElements first, it will result in that their related
/*	Links are deleted because GElements/Links closed consistency, thus
/*	selectedItems maybe are changed to suspended pointers.
/*	The Key Problem is that selectedItems hold a state before QGraphicsItems
/*	being deleted from QGraphicsScene.
/************************************************************************/

APnMApp::~APnMApp()
{
	//arrow will delete its reference from its startItem and endItem
	qDeleteAll(mLinks);
	qDeleteAll( mElements );
}

void APnMApp::deleteItems(QList<QGraphicsItem*> items)
{
	QList<QGraphicsItem*>::iterator it = items.begin();
	while (it != items.end()) {
		Link* link = dynamic_cast<Link*>(*it);
		if (link) {
			deleteLink(link);
			it = items.erase(it);
		}else
			++it;
	}
	
	//will delete some GElements' child QGraphicsItem
	//qDeleteAll(items); 
	GElement* full;
	foreach(QGraphicsItem* item, items){
		full = dynamic_cast<GElement*>(item); //can't use qgraphicsitem_cast
		if (full)
			deleteElement(full);
	}
}

void APnMApp::selectAll()
{
	foreach(GElement* ele, mElements) 
		ele->setSelected(true);
}

void APnMApp::updateAllLinks()
{
	foreach (Link* link, mLinks)
		link->updatePosition();
}

void APnMApp::setScene(APnMScene* aScene)
{
	mScene = aScene;
}

APnMScene* APnMApp::scene() const
{
	return mScene;
}

bool APnMApp::addElement(GElement* newElement)
{
	//test whether there is a name be used more than one time
	//if (mElements.contains(newElement->elementName()))
	//	return false;

	mElements.append(newElement);
	if (mScene) 
		mScene->addItem(newElement);
	return true;
}

bool APnMApp::deleteElement(GElement* aElement)
{
	//if ( !mElements.contains( aElement->elementName()) ) 
	//	return false;

	foreach (Link* link, aElement->allLinks()) 
		deleteLink(link);

	mElements.removeAll(aElement);
	delete aElement;
	return true;
}

void APnMApp::addLink(Link* newLink)
{
	mLinks.append(newLink);

	if (mScene) {
		mScene->addItem(newLink);
		newLink->updatePosition();
	}
}

void APnMApp::deleteLink(Link* aLink)
{
	mLinks.removeAt( mLinks.indexOf(aLink) );
	delete aLink;
}

void APnMApp::loadFromXml(const QDomDocument& impDoc, const QDomDocument& layDoc)
{

	loadImp(impDoc);
	loadLay(layDoc);

	if (mScene) {
		foreach(GElement* g, mElements)
			mScene->addItem(g);
		foreach(Link* link, mLinks) {
			mScene->addItem(link);
			link->updatePosition();
		}
	}
}

/************************************************************************
** Should first output GElements, and then Links.
** Cooperate with read from File.
************************************************************************/
void APnMApp::saveToXml(QXmlStreamWriter& impWriter, QXmlStreamWriter& layWriter)
{
	saveImp(impWriter);
	saveLay(layWriter);
}

//parse the token data for the application
void APnMApp::loadData(QDomElement& tokenElement, GElement* ele)
{
	while (!tokenElement.isNull()) {
		Token *token = new Token();
		token->parseContent(tokenElement);
		ele->addToken(token);

		tokenElement = tokenElement.nextSiblingElement();
	}
}

void APnMApp::saveData(QXmlStreamWriter& dataWriter, const GElement* ele)
{
	QList<Token*> tokens = ele->tokens();
	foreach (Token *token, tokens) 
		token->writeContent(dataWriter);
}

GElement* APnMApp::findElement(const QString& name) const
{
	foreach (GElement* ele, mElements)
		if (ele->elementName()==name) 
			return ele;
	return 0;
}

void APnMApp::loadImp(const QDomDocument& impDoc)
{
	QDomElement impRoot = impDoc.documentElement();
	DElement* d;
	GElement* g;
	GActiveElement* active;
	GPool* pool;
	Link* link;
	QDomElement subElement = impRoot.firstChildElement();
	while (!subElement.isNull()) {
		if (subElement.nodeName() != "link") {
			d = DElementDelegate::instance()->find(subElement.nodeName(), subElement.attribute("class"));
			g = d->createGraphics();
			if (!g) {
				qWarning("There is no Element with given name and class.\n"
					"\tThe given name is %1, class is %2", 
					qPrintable(subElement.nodeName()),
					qPrintable(subElement.attribute("class")));
				return;
			}
			g->setElementName(subElement.attribute("name"));
			mElements.append(g);

			loadData(subElement.firstChildElement(), g);//read tokens
		}else {
			g = findElement(subElement.attribute("active"));
			active = qgraphicsitem_cast<GActiveElement*>(g);
			g = findElement(subElement.attribute("pool"));
			pool = qgraphicsitem_cast<GPool*>(g);
			link = new Link(active, pool, subElement.attribute("port"));
			mLinks.append(link);
		}
		subElement = subElement.nextSiblingElement();
	}
}

void APnMApp::loadLay(const QDomDocument& layDoc)
{
	QDomElement layRoot = layDoc.documentElement();
	QDomElement subElement = layRoot.firstChildElement();
	qreal x,y;
	while (!subElement.isNull()) {
		x = subElement.attribute("x").toDouble();
		y = subElement.attribute("y").toDouble();
		findElement(subElement.nodeName())->setPos(x,y);
		subElement = subElement.nextSiblingElement();
	}
}

void APnMApp::saveImp(QXmlStreamWriter& impWriter)
{
	impWriter.writeStartDocument();
	impWriter.writeStartElement("composite");
	impWriter.writeAttribute("class", "CompositeActor");

	foreach(GElement* g, mElements) {
		impWriter.writeStartElement(g->kind());
		impWriter.writeAttribute("name", g->elementName());
		impWriter.writeAttribute("class", g->className());

		saveData(impWriter, g);//save tokens
		
		impWriter.writeEndElement();
	}

	foreach(Link* l, mLinks) {
		impWriter.writeStartElement("link");
		impWriter.writeAttribute("active", l->startObject());
		impWriter.writeAttribute("port", l->startPort());
		impWriter.writeAttribute("pool", l->endObject());
		impWriter.writeEndElement();
	}
	impWriter.writeEndDocument();

}

void APnMApp::saveLay(QXmlStreamWriter& layWriter)
{
	layWriter.writeStartDocument();	
	layWriter.writeStartElement("composite");
	layWriter.writeAttribute("class", "CompositeActor");

	foreach(GElement* g, mElements) {
		layWriter.writeStartElement(g->elementName());
		layWriter.writeAttribute("x", QString("%1").arg(g->pos().x()));
		layWriter.writeAttribute("y", QString("%1").arg(g->pos().y()));
		layWriter.writeEndElement();
	}
	layWriter.writeEndDocument();
}